5 * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @link http://www.phpunit.de/
44 * @since File available since Release 3.2.0
47 require_once 'PHPUnit/Runner/Version.php';
48 require_once 'PHPUnit/Util/Metrics/Project.php';
49 require_once 'PHPUnit/Util/Log/PMD/Rule/Class.php';
50 require_once 'PHPUnit/Util/Log/PMD/Rule/File.php';
51 require_once 'PHPUnit/Util/Log/PMD/Rule/Function.php';
52 require_once 'PHPUnit/Util/Log/PMD/Rule/Project.php';
53 require_once 'PHPUnit/Util/Class.php';
54 require_once 'PHPUnit/Util/CodeCoverage.php';
55 require_once 'PHPUnit/Util/Filter.php';
56 require_once 'PHPUnit/Util/FilterIterator.php';
57 require_once 'PHPUnit/Util/Printer.php';
59 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
62 * Generates an XML logfile with software metrics information using the
63 * PMD format "documented" at
64 * http://svn.atlassian.com/fisheye/browse/~raw,r=7084/public/contrib/bamboo/bamboo-pmd-plugin/trunk/src/test/resources/test-pmd-report.xml
68 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
69 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
70 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
71 * @version Release: 3.3.17
72 * @link http://www.phpunit.de/
73 * @since Class available since Release 3.2.0
75 class PHPUnit_Util_Log_PMD extends PHPUnit_Util_Printer
79 protected $rules = array(
90 * @param array $configuration
91 * @throws InvalidArgumentException
93 public function __construct($out = NULL, array $configuration = array())
95 parent::__construct($out);
96 $this->loadClasses($configuration);
100 * @param PHPUnit_Framework_TestResult $result
102 public function process(PHPUnit_Framework_TestResult $result)
104 $codeCoverage = $result->getCodeCoverageInformation();
105 $summary = PHPUnit_Util_CodeCoverage::getSummary($codeCoverage);
106 $files = array_keys($summary);
107 $metrics = new PHPUnit_Util_Metrics_Project($files, $summary);
109 $document = new DOMDocument('1.0', 'UTF-8');
110 $document->formatOutput = TRUE;
112 $pmd = $document->createElement('pmd');
113 $pmd->setAttribute('version', 'PHPUnit ' . PHPUnit_Runner_Version::id());
114 $document->appendChild($pmd);
116 foreach ($this->rules['project'] as $ruleName => $rule) {
117 $result = $rule->apply($metrics);
119 if ($result !== NULL) {
128 foreach ($metrics->getFiles() as $fileName => $fileMetrics) {
129 $xmlFile = $document->createElement('file');
130 $xmlFile->setAttribute('name', $fileName);
132 $this->added = FALSE;
134 foreach ($this->rules['file'] as $ruleName => $rule) {
135 $result = $rule->apply($fileMetrics);
137 if ($result !== NULL) {
148 foreach ($fileMetrics->getClasses() as $className => $classMetrics) {
149 if (!$classMetrics->getClass()->isInterface()) {
150 $classStartLine = $classMetrics->getClass()->getStartLine();
151 $classEndLine = $classMetrics->getClass()->getEndLine();
152 $classPackage = $classMetrics->getPackage();
154 foreach ($this->rules['class'] as $ruleName => $rule) {
155 $result = $rule->apply($classMetrics);
157 if ($result !== NULL) {
172 foreach ($classMetrics->getMethods() as $methodName => $methodMetrics) {
173 if (!$methodMetrics->getMethod()->isAbstract()) {
174 $this->processFunctionOrMethod($xmlFile, $methodMetrics, $classPackage);
180 foreach ($fileMetrics->getFunctions() as $functionName => $functionMetrics) {
181 $this->processFunctionOrMethod($xmlFile, $functionMetrics);
185 $pmd->appendChild($xmlFile);
189 $this->write($document->saveXML());
194 * @param string $violation
195 * @param DOMElement $element
196 * @param PHPUnit_Util_Log_PMD_Rule $rule
197 * @param integer $line
198 * @param integer $toLine
199 * @param string $package
200 * @param string $class
201 * @param string $method
203 protected function addViolation($violation, DOMElement $element, PHPUnit_Util_Log_PMD_Rule $rule, $line = '', $toLine = '', $package = '', $class = '', $method = '', $function = '')
205 $violationXml = $element->appendChild(
206 $element->ownerDocument->createElement('violation', $violation)
209 $violationXml->setAttribute('rule', $rule->getName());
210 $violationXml->setAttribute('priority', $rule->getPriority());
213 $violationXml->setAttribute('line', $line);
216 if (!empty($toLine)) {
217 $violationXml->setAttribute('to-line', $toLine);
220 if (empty($package)) {
224 if (!empty($package)) {
225 $violationXml->setAttribute('package', $package);
228 if (!empty($class)) {
229 $violationXml->setAttribute('class', $class);
232 if (!empty($method)) {
233 $violationXml->setAttribute('method', $method);
236 if (!empty($function)) {
237 $violationXml->setAttribute('function', $function);
241 protected function processFunctionOrMethod(DOMElement $element, $metrics, $package = '')
245 if ($metrics->getFunction() instanceof ReflectionMethod) {
246 $scope = $metrics->getFunction()->getDeclaringClass()->getName();
249 $startLine = $metrics->getFunction()->getStartLine();
250 $endLine = $metrics->getFunction()->getEndLine();
251 $name = $metrics->getFunction()->getName();
253 foreach ($this->rules['function'] as $ruleName => $rule) {
254 $result = $rule->apply($metrics);
256 if ($result !== NULL) {
273 protected function loadClasses(array $configuration)
275 $basedir = dirname(__FILE__) . DIRECTORY_SEPARATOR .
276 'PMD' . DIRECTORY_SEPARATOR . 'Rule';
279 $basedir . DIRECTORY_SEPARATOR . 'Class',
280 $basedir . DIRECTORY_SEPARATOR . 'File',
281 $basedir . DIRECTORY_SEPARATOR . 'Function',
282 $basedir . DIRECTORY_SEPARATOR . 'Project'
285 foreach ($dirs as $dir) {
286 if (file_exists($dir)) {
287 $iterator = new PHPUnit_Util_FilterIterator(
288 new RecursiveIteratorIterator(
289 new RecursiveDirectoryIterator($dir)
294 foreach ($iterator as $file) {
295 include_once $file->getPathname();
300 $classes = get_declared_classes();
302 foreach ($classes as $className) {
303 $class = new ReflectionClass($className);
305 if (!$class->isAbstract() && $class->isSubclassOf('PHPUnit_Util_Log_PMD_Rule')) {
306 $rule = explode('_', $className);
307 $rule = $rule[count($rule)-1];
309 if (isset($configuration[$className])) {
310 $object = new $className(
311 $configuration[$className]['threshold'],
312 $configuration[$className]['priority']
315 $object = new $className;
318 if ($class->isSubclassOf('PHPUnit_Util_Log_PMD_Rule_Project')) {
319 $this->rules['project'][$rule] = $object;
322 if ($class->isSubclassOf('PHPUnit_Util_Log_PMD_Rule_File')) {
323 $this->rules['file'][$rule] = $object;
326 else if ($class->isSubclassOf('PHPUnit_Util_Log_PMD_Rule_Class')) {
327 $this->rules['class'][$rule] = $object;
330 else if ($class->isSubclassOf('PHPUnit_Util_Log_PMD_Rule_Function')) {
331 $this->rules['function'][$rule] = $object;